/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO

Authors: Nitay Megides
		 Rachel Banino

Project: Exercise 3

Using:	Manager.h
		General.h
		hw_code\Bmp24LineReader.h
		
Description:
Locator locates faces in the picture. It loads the picture line by line, 
and whenever it has enough lines to work with, it scans for faces. The
described process works paralelly on different areas, on different threads.
This module works tightly with the loader.

oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "General.h"
#include "Locator.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "hw_code\IspImageAnalysis.h"
#include "Comparer.h"

// The struct each locator thread receives
typedef struct 
{
	HANDLE ThreadHandle;
	DWORD ThreadID;
	TArea Workarea;
} TLocatorInfo;
typedef TLocatorInfo* PLocatorInfo;

// An array of the locators
PLocatorInfo Locators;
// How many locator we have
int LocatorCount;

// Forward decl. to prevent a warning
DWORD WINAPI LocatorMain(void* ptr);


BOOL LocatorInitialize(PArea areas, int areasCount)
{
	int i = 0;

	LocatorCount = areasCount;

	// Allocate enough space
	Locators = (PLocatorInfo)malloc(LocatorCount * sizeof(TLocatorInfo));
	if (Locators == NULL)
	{
		REPORT_ERROR("Locator: Out of memory\n");
		return FALSE;
	}
	
	// Initialize everything
	for (i = 0; i < LocatorCount; i++)
	{
		Locators[i].Workarea = areas[i];
		// First initialize values to ease the freeing in case of an error
		Locators[i].ThreadHandle = NULL;

		// Then allocate everything
		Locators[i].ThreadHandle = CreateThread(NULL, 0, LocatorMain, &(Locators[i]), CREATE_SUSPENDED, &(Locators[i].ThreadID));
		if (Locators[i].ThreadHandle == NULL)
		{
			REPORT_ERROR("Locator: Couldn't create thread\n");
			return FALSE;
		}
	}

	return TRUE;
}

void LocatorFree()
{
	int i = 0;
	HANDLE* threadHandles = (HANDLE*)malloc(LocatorCount * sizeof(HANDLE));
	if (threadHandles == NULL)
	{
		REPORT_ERROR("Locator: Out of memory while freeing. Did not close all handles\n");
		return;
	}

	// Wait for everyone to die out
	for (i = 0; i < LocatorCount; i++)
	{
		threadHandles[i] = Locators[i].ThreadHandle;
	}
	if (WaitForMultipleMultipleObjects(LocatorCount, threadHandles, THREAD_TERMINATE_TIMEOUT) == WAIT_TIMEOUT)
	{
		// Threads are zombies! Kill 'em all!
		REPORT_ERROR("Locator: Threads did not finish properly\n");

		for (i = 0; i < LocatorCount; i++)
		{
			// Terminate all threads. I don't really mind what the outcome is
			TerminateThread(threadHandles[i], -1);
		}
	}

	// Clean up
	for (i = 0; i < LocatorCount; i++)
	{
		if (Locators[i].ThreadHandle == NULL)
			CloseHandle(Locators[i].ThreadHandle);
	}

	free(Locators);
	free(threadHandles);
}

void LocatorStartWorking()
{
	int i = 0;

	for (i = 0; i < LocatorCount; i++)
	{
		ResumeThread(Locators[i].ThreadHandle);
	}
}

BOOL LocatorWaitForFinish(int timeout)
{
	int i;
	HANDLE* threadHandles = (HANDLE*)malloc(LocatorCount * sizeof(HANDLE));
	DWORD waitResult = 0;

	if (threadHandles == NULL)
	{
		REPORT_ERROR("Locator: Out of memory while freeing. Did not close all handles\n");
		return FALSE;
	}

	for (i = 0; i < LocatorCount; i++)
	{
		threadHandles[i] = Locators[i].ThreadHandle;
	}

	waitResult = WaitForMultipleMultipleObjects(LocatorCount, threadHandles, timeout);
	free(threadHandles);
	return (waitResult == WAIT_TIMEOUT);
}

DWORD WINAPI LocatorMain(void* ptr)
{
	PLocatorInfo locatorInfo = (PLocatorInfo)ptr;
	PArea workArea;
	TArea face;
	int currentLine;
	int positionX = 0;

	workArea = &(locatorInfo->Workarea);
	currentLine = workArea->Y;

	REPORT_DEBUG(
		"Locator started on area %02d\n", workArea->Index);

	// Wait until enough lines have been read (Notice: Lines exceeding the picture 
	// boundaries will return immediately)
	if (!WaitLineRead(currentLine + GlobalParameters.FaceHeight + 1))
	{
		REPORT_ERROR("Locator: Error waiting for the next line!\n");
		return -1;
	}

	// Loop until the end of the area or until the end 
	while ((!ProgramTerminated()) && (currentLine < (workArea->Y + workArea->Height)))
	{
		// Wait for permission
		WaitForRunPermission();

		for (positionX = workArea->X; positionX < (workArea->X + workArea->Width); positionX++)
		{
			face.X = positionX;
			face.Y = currentLine;
			face.Width = GlobalParameters.FaceWidth;
			face.Height = GlobalParameters.FaceHeight;

			// Try and locate a face
			if (BoxContainsAFace(
				GlobalPicture.Picture,
				face.Y, 
				face.X, 
				face.Height, 
				face.Width, 
				GlobalPicture.PictureHeight,
				GlobalPicture.PictureWidth))
			{
				// Add to facebook and such
				if (!FacebookAddAndCompare(&face))
				{
					REPORT_ERROR("Locator: Error adding face to facebook\n");
					break;
				}
			}
		}
		
		ReleaseRunPermission();

		// Wait for the next line
		currentLine++;
		if (!WaitLineRead(currentLine + GlobalParameters.FaceHeight + 1))
		{
			REPORT_ERROR("Locator: Error waiting for the next line!\n");
			return -1;
		}
	}


	REPORT_DEBUG(
		"Locator done on area %02d. Face Count: %d\n", 
		workArea->Index,
		FacesCount());

	return 0;
}
